package mapper

import (
	"errors"
	"fmt"
	"log"
	"reflect"

	"gitee.com/rainsuperman/mapper_orm/mapper_core"
)

// 插入数据
// 返回插入的ID
func Insert(entity DbEntity) int64 {
	if GetConfig().DefaultSqlDB == nil {
		panic("DefaultSqlDB is nil")
	}
	id := InsertMust(GetConfig().DefaultSqlDB, entity)
	return id
}

// 插入数据
// 返回插入的ID
func InsertMust(dbOrTxObj any, entity DbEntity) int64 {

	id, err := InsertWithErr(dbOrTxObj, entity)
	if err != nil {
		panic(err)
	}
	return id
}

// 插入数据
// 返回插入的ID
func InsertWithErr(dbOrTxObj any, entity DbEntity) (int64, error) {

	valueList := []any{}

	reflectType := reflect.TypeOf(entity).Elem()

	reflectValueType := reflect.ValueOf(entity).Elem()

	var idValue reflect.Value

	insertIntoStr := "INSERT INTO " + entity.TableName() + " ("
	valuesStr := " VALUES ("

	tableInfos := mapper_core.GetTabelInfo(reflectType)

	for i := range tableInfos {

		if tableInfos[i].ColumnName == GetConfig().IdColumnName {
			idValue = reflectValueType.FieldByName(tableInfos[i].FieldName)
		}

		va := getValue(tableInfos[i].FieldName, reflectValueType)
		if va == nil { //空值不插入
			continue
		}
		valueList = append(valueList, *va)
		insertIntoStr += tableInfos[i].ColumnName + ","
		valuesStr += "?,"
	}

	insertIntoStr = insertIntoStr[:len(insertIntoStr)-1] //去除最后一个逗号
	valuesStr = valuesStr[:len(valuesStr)-1]             //去除最后一个逗号

	insertIntoStr = insertIntoStr + ")"
	valuesStr = valuesStr + ")"

	if GetConfig().Debug {
		log.Println(insertIntoStr + valuesStr)
		log.Println(valueList)
	}

	lId, er := mapper_core.DoExecWithLastInsertId(dbOrTxObj, insertIntoStr+valuesStr, valueList...)
	if er != nil {
		return 0, er
	}

	idTypeValue := reflect.New(idValue.Type().Elem()) // 创建一个新的字符串类型的零值
	idTypeValue.Elem().SetInt(lId)                    // 设置新字符串的值

	idValue.Set(idTypeValue)

	return lId, nil
}

// 批量插入数据
// 要求entities必须是同类型
// 返回插入成功条数
func InsertBatch(entities []DbEntity) int64 {

	if GetConfig().DefaultSqlDB == nil {
		panic("DefaultSqlDB is nil")
	}

	return InsertBatchMust(GetConfig().DefaultSqlDB, entities)
}

// 批量插入数据
// 要求entities必须是同类型
// 返回插入成功条数
func InsertBatchMust(dbOrTxObj any, entities []DbEntity) int64 {

	id, err := InsertBatchWithErr(dbOrTxObj, entities)
	if err != nil {
		panic(err)
	}
	return id
}

// 批量插入数据
// 要求entities必须是同类型
// 返回插入成功条数
func InsertBatchWithErr(dbOrTxObj any, entities []DbEntity) (int64, error) {

	valueList := []any{}

	reflectType := reflect.TypeOf(entities[0]).Elem()
	finalStr := ""
	firstTableName := entities[0].TableName()
	insertIntoStr := "INSERT INTO " + firstTableName + " ("

	for ii, entity := range entities {

		if entity.TableName() != firstTableName {
			return 0, errors.New("批量插入需同类型")
		}

		reflectValueType := reflect.ValueOf(entity).Elem()
		tableInfos := mapper_core.GetTabelInfo(reflectType)

		valuesStr := "("
		for i := range tableInfos {

			va := getValue(tableInfos[i].FieldName, reflectValueType)
			if va == nil { //空值不插入
				continue
			}
			valueList = append(valueList, *va)
			if ii == 0 {
				insertIntoStr += tableInfos[i].ColumnName + ","
			}
			valuesStr += "?,"
		}

		if ii == 0 {
			insertIntoStr = insertIntoStr[:len(insertIntoStr)-1] //去除最后一个逗号
			insertIntoStr = insertIntoStr + ") VALUES "
		}
		valuesStr = valuesStr[:len(valuesStr)-1] //去除最后一个逗号
		valuesStr = valuesStr + "),"

		if ii == 0 {
			finalStr += insertIntoStr + valuesStr
		} else {
			finalStr += valuesStr
		}
	}
	finalStr = finalStr[:len(finalStr)-1] //去除最后一个逗号

	if GetConfig().Debug {
		log.Println(finalStr)
		log.Println(valueList)
	}
	rrr, er := mapper_core.DoExecWithEffectNum(dbOrTxObj, finalStr, valueList...)

	if er != nil {

		return 0, er
	}

	return rrr, nil
}

// 根据ID更新数据
// 返回影响行数和错误信息
func UpdateById(entity DbEntity) int64 {

	if GetConfig().DefaultSqlDB == nil {
		panic("DefaultSqlDB is nil")
	}

	return UpdateByIdMust(GetConfig().DefaultSqlDB, entity)

}

// 根据ID更新数据
// 返回影响行数和错误信息
func UpdateByIdMust(dbOrTxObj any, entity DbEntity) int64 {

	tt, err := UpdateByIdWithErr(dbOrTxObj, entity)
	if err != nil {
		panic(err)
	}

	return tt

}

// 根据ID更新数据
// 返回影响行数和错误信息
// 错误信息为nil时表示更新成功
func UpdateByIdWithErr(dbOrTxObj any, entity DbEntity) (int64, error) {

	valueList := []any{}

	//判断entity是否为指针类型
	reflectTypePtr := reflect.TypeOf(entity)
	if reflectTypePtr.Kind() != reflect.Ptr {
		return 0, errors.New("entity必须为指针类型")
	}

	reflectType := reflectTypePtr.Elem()

	reflectValueType := reflect.ValueOf(entity).Elem()

	var idData any
	var verData *int64

	tableInfos := mapper_core.GetTabelInfo(reflectType)

	sqlStart := "UPDATE " + entity.TableName() + " SET "

	for i := range tableInfos {
		curVer := false

		va := getValue(tableInfos[i].FieldName, reflectValueType)

		//主键字段处理
		if tableInfos[i].ColumnName == GetConfig().IdColumnName {
			if va == nil {
				return 0, errors.New("id不能为空")
			}

			idData = *va
			continue
		}

		//乐观锁字段处理
		if tableInfos[i].ColumnName == GetConfig().OptimisticLockName && len(GetConfig().OptimisticLockName) > 0 {

			if va == nil {
				return 0, errors.New("乐观锁字段不能为空")
			}

			//乐观锁

			v, ok := (*va).(int64)
			if !ok {
				v1, ok1 := (*va).(int32)
				if !ok1 {

					v2, ok2 := (*va).(int)
					if !ok2 {
						return 0, errors.New("乐观锁字段必须为整形类型")
					}
					v = int64(v2)
				}
				v = int64(v1)
			}
			verData = &v
			curVer = true

		}

		if va == nil {
			continue
		}

		if !curVer {
			valueList = append(valueList, *va)
		} else {
			valueList = append(valueList, *verData+1)
		}

		sqlStart += tableInfos[i].ColumnName + "=?,"
	}

	sqlStart = sqlStart[:len(sqlStart)-1] //去除最后一个逗号
	sqlEnd := ""
	if verData != nil && len(GetConfig().OptimisticLockName) > 0 {

		sqlEnd = " WHERE " + GetConfig().IdColumnName + "=? AND " + GetConfig().OptimisticLockName + "=?"
	} else {
		sqlEnd = " WHERE " + GetConfig().IdColumnName + "=? "
	}

	valueList = append(valueList, idData) //主键字段

	if verData != nil {

		valueList = append(valueList, verData) //乐观锁字段
	}

	if GetConfig().Debug {
		log.Println(sqlStart + sqlEnd)
		log.Println(valueList)
	}

	rrr, er := mapper_core.DoExecWithEffectNum(dbOrTxObj, sqlStart+sqlEnd, valueList...)

	if er != nil {
		return 0, er
	}

	if verData != nil {
		if rrr == 0 {
			return 0, errors.New("乐观锁生效，数据已被修改")
		}

	}

	return rrr, er

}

// 根据ID删除数据，真实删除
// 返回影响行数和错误信息
// 错误信息为nil时表示删除成功
func DelByIdReal[T DbEntity](id int64) int64 {

	if GetConfig().DefaultSqlDB == nil {
		panic("DefaultSqlDB is nil")
	}

	return DelByIdRealMust[T](GetConfig().DefaultSqlDB, id)
}

// 根据ID删除数据，真实删除
// 返回影响行数和错误信息
// 错误信息为nil时表示删除成功
func DelByIdRealMust[T DbEntity](dbOrTxObj any, id int64) int64 {

	tt, err := DelByIdRealWithErr[T](dbOrTxObj, id)
	if err != nil {
		panic(err)
	}

	return tt
}

// 根据ID删除数据，真实删除
// 返回影响行数和错误信息
// 错误信息为nil时表示删除成功
func DelByIdRealWithErr[T DbEntity](dbOrTxObj any, id int64) (int64, error) {

	var t T

	sqlStart := "DELETE FROM "

	sqlStart = sqlStart + t.TableName()

	sqlEnd := " WHERE " + GetConfig().IdColumnName + "=? "

	if GetConfig().Debug {
		log.Println(sqlStart + sqlEnd)
	}

	rrr, er := mapper_core.DoExecWithEffectNum(dbOrTxObj, sqlStart+sqlEnd, id)

	if er != nil {
		fmt.Println(er)
		return rrr, er
	}

	return rrr, er
}

// 根据ID删除数据，逻辑删除
// 可以配置逻辑删除的字段名默认为del
// 会将del字段的值设置为id值
// 返回影响行数和错误信息
// 错误信息为nil时表示删除成功
func DelById[T DbEntity](id int64) int64 {

	if GetConfig().DefaultSqlDB == nil {
		panic("DefaultSqlDB is nil")
	}

	return DelByIdMust[T](GetConfig().DefaultSqlDB, id)
}

// 根据ID删除数据，逻辑删除
// 可以配置逻辑删除的字段名默认为del
// 会将del字段的值设置为id值
// 返回影响行数和错误信息
// 错误信息为nil时表示删除成功
func DelByIdMust[T DbEntity](dbOrTxObj any, id int64) int64 {

	tt, err := DelByIdWithErr[T](dbOrTxObj, id)
	if err != nil {
		panic(err)
	}

	return tt
}

// 根据ID删除数据，逻辑删除
// 可以配置逻辑删除的字段名默认为del
// 会将del字段的值设置为id值
// 返回影响行数和错误信息
// 错误信息为nil时表示删除成功
func DelByIdWithErr[T DbEntity](dbOrTxObj any, id int64) (int64, error) {

	var t T

	sqlStart := "UPDATE "

	re := t.TableName()

	sqlStart = sqlStart + re

	sqlStart = sqlStart + " SET " + GetConfig().MarkDelColumnName + "=" + GetConfig().IdColumnName

	sqlEnd := " WHERE " + GetConfig().IdColumnName + "=? "

	if GetConfig().Debug {
		log.Println(sqlStart + sqlEnd)
		log.Println(id)
	}

	rrr, er := mapper_core.DoExecWithEffectNum(dbOrTxObj, sqlStart+sqlEnd, id)

	if er != nil {
		fmt.Println(er)
		return 0, er
	}

	return rrr, er
}
